# -*- encoding:utf-8 -*-

# a gui for card scan process
import sys
from PyQt4 import QtGui, QtCore
import cv2
import time
import cardprepare
import cardscan
import xlsxwriter
import numpy as np
import logging

logging.basicConfig(level=logging.DEBUG)

class CameraThread(QtCore.QThread):
    def __init__(self, parent=None, fps=30):
        super().__init__(parent)
        self.fps = fps
        self.running = True

    def run(self):
        while self.running:
            self.parent().update_video.emit()
            time.sleep(1/self.fps)

    def stop(self):
        self.running = False

class VideoWidgets(QtGui.QLabel):
    update_video = QtCore.pyqtSignal()
    def __init__(self, parent=None, cameraid = 0, videowidth=640, videoheight=480, fps=30):
        super().__init__(parent)
        self.videowidth = videowidth
        self.videoheight = videoheight
        self.fps = fps
        self.running = False
        self.task = None

        self.image = None

        self.init_ui()
        self.init_camera(cameraid)
        self.create_invailed_image()

        self.update_video.connect(self.capture)

    def create_invailed_image(self):
        w, h = self.videowidth, self.videoheight
        pixmap = QtGui.QPixmap(w, h)
        qpainter = QtGui.QPainter(pixmap)
        qpainter.setPen(QtCore.Qt.red)
        thick = 5
        qpainter.drawRect(thick, thick, w-2*thick, h-2*thick)
        qpainter.drawLine(thick,thick, w-thick, h-thick)
        qpainter.drawLine(w-thick,thick, thick, h-thick)
        self.invailed = pixmap

    def init_ui(self):
        pixmap = QtGui.QPixmap(self.videowidth, self.videoheight)
        pixmap.fill()
        self.setPixmap(pixmap)

    def init_camera(self, cameraid):
        cap = cv2.VideoCapture(cameraid)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.videoheight)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.videowidth)
        logging.debug("camera #{}: {}".format(cameraid, cap.isOpened()))
        self.cap = cap

    def is_valid(self):
        try:
            return self.cap.isOpened()
        except:
            return False

    def start_capture(self):
        if self.task is None or not self.task.isRunning():
            self.task = CameraThread(parent=self, fps=self.fps)
            self.task.start()
            self.running = True
            logging.debug("task started")

    def stop_capture(self):
        self.running = False
        if self.task and self.task.isRunning():
            self.task.stop()
            self.task.quit()
            logging.debug("task stoped")

    def capture(self):
        cap = self.cap
        success, frame = cap.read()
        if success:
            if self.running:
                self.image = frame
                pixmap = self.cv2_pixmap(frame)
                self.setPixmap(pixmap)
        else:
            self.setPixmap(self.invailed)
            self.stop_capture()

    def cv2_pixmap(self, cv2image):
        ndim = cv2image.ndim
        if ndim == 2:
            h, w = cv2image.shape
            d = 1
            fmt = QtGui.QImage.Format_Indexed8
        elif ndim == 3:
            h, w, d = cv2image.shape
            cv2image = cv2image[:,:,::-1]
            fmt = QtGui.QImage.Format_RGB888
        qimage = QtGui.QImage(cv2image.tobytes(), w, h, d*w, fmt)
        return QtGui.QPixmap(qimage)

    def close_camera(self):
        self.running = False
        self.stop_capture()

class LazyVideoWidgets(VideoWidgets):
    def __init__(self, parent=None, cameraid = 0, videowidth=640, videoheight=480, fps=30):
        super().__init__(parent, cameraid, videowidth, videoheight, fps)
        self.thumb = None

    def start_capture(self):
        super().start_capture()
        self.thumb = None

    def capture(self):
        cap = self.cap
        success, frame = cap.read()
        if success:
            if self.running:
                h, w, d = frame.shape
                self.image = frame
                thumb = cv2.resize(frame, (8,8))
                thumb = cv2.cvtColor(thumb, cv2.COLOR_BGR2GRAY)
                thumb.reshape(-1)
                avg = np.mean(thumb)
                thumb = thumb < avg
                thumb = thumb.tostring()
                if self.thumb is None or not self.issimilar(thumb, self.thumb, tol=1):
                    pixmap = self.cv2_pixmap(frame)
                    self.setPixmap(pixmap)
                    self.thumb = thumb
        else:
            self.setPixmap(self.invailed)
            self.stop_capture()

    def issimilar(self, thumb1, thumb2, tol = 5):
        num_diff = 0
        for x, y in zip(thumb1, thumb2):
            if x != y:
                num_diff += 1
        return  num_diff <= tol

def AutoVideoWidgets(LazyVideoWidges):
    def __init__(self, parent=None, cameraid = 0, videowidth=640, videoheight=480, fps=30):
        super().__init__(parent, cameraid, videowidth, videoheight, fps)


class CardScanner(QtGui.QMainWindow):
    IMAGE_WIDTH = 640
    IMAGE_HEIGHT = 480
    def __init__(self, parent=None, cardreader=None):
        super().__init__(parent)
        self.cid = 1
        self.init_ui()
        self.card_image = None
        self.cardreader=None
        self.valid_camera()
        self.image_show.start_capture()

    def valid_camera(self):
        if not self.image_show.is_valid():
            for i in range(10):
                self.cameraid.setValue(i)
                if self.image_show.is_valid():
                    break
            else:
                logging.debug("no valid")



    def init_ui(self):
        center = QtGui.QWidget()
        self.setCentralWidget(center)

        hbox = QtGui.QHBoxLayout()
        self.image_show = VideoWidgets(cameraid=self.cid, fps=10)
        hbox.addWidget(self.image_show)

        '''
        scrollarea = QtGui.QScrollArea()
        hbox.addWidget(scrollarea)
        panel = QtGui.QWidget()
        '''
        grid = QtGui.QGridLayout()
        hbox.addLayout(grid)
        row = 0
        grid.addWidget(QtGui.QLabel("摄像头号"), row, 0)
        self.cameraid = QtGui.QSpinBox(value=self.cid)
        self.cameraid.valueChanged.connect(self.change_camera)
        grid.addWidget(self.cameraid, row, 1)
        row += 1
        '''
        self.start = QtGui.QPushButton("开始")
        self.start.clicked.connect(self.image_show.start_capture)
        grid.addWidget(self.start, row, 0)
        row += 1
        '''

        self.stop = QtGui.QPushButton("捕捉")
        self.stop.setMinimumHeight(200)
        self.stop.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
        self.stop.clicked.connect(self.capture_card)
        grid.addWidget(self.stop, row, 0, row, 2)
        self.stop.setFocus()
        row += 1
        '''
        self.read = QtGui.QPushButton("读取")
        self.read.clicked.connect(self.read_card)
        grid.addWidget(self.read, row, 0)
        row += 1
        '''
        self.save = QtGui.QPushButton("保存")
        self.save.setMinimumHeight(200)
        self.save.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
        self.save.clicked.connect(self.save_test)
        grid.addWidget(self.save, row, 0, row, 2)
        '''
        row += 1
        grid.addWidget(QtGui.QLabel(), row, 0, row, 2)
        panel.setLayout(grid)
        scrollarea.setWidget(panel)
        '''
        vbox = QtGui.QVBoxLayout()
        vbox.addLayout(hbox)
        result_panel = QtGui.QGridLayout()
        self.list = QtGui.QTableWidget()
        result_panel.addWidget(self.list, 0, 0)
        vbox.addLayout(result_panel)
        center.setLayout(vbox)

    def connect_all(self):
        pass

    def closeEvent(self, e):
        logging.debug("close main")
        self.image_show.close_camera()
        e.accept()

    def change_camera(self, id):
        self.image_show.stop_capture()
        self.image_show.init_camera(id)
        self.cid = id
        self.image_show.start_capture()

    def capture_card(self):
        self.image_show.stop_capture()
        time.sleep(1)
        image = self.image_show.image
        if image.ndim == 3:
            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        image = cardprepare.prepare_image(image, True)
        self.card_image = image
        pixmap = self.image_show.cv2_pixmap(image)
        self.image_show.setPixmap(pixmap)
        self.read_card()

    def read_card(self):
        if self.card_image is not None:
            row = self.list.rowCount()
            snum, answers = cardreader.read_card(self.card_image)
            # 加入对学号合法性的检测
            if '#' in snum:
                QtGui.QMessageBox.warning(self, '','非法学号：'+snum)
            # 是否重复
            snums = [self.list.item(i, 0).text() for i in range(row)]
            logging.debug('学号:' + str(snums))
            if snum in snums :
                trow = snums.index(snum)
                msgbox = QtGui.QMessageBox(self)
                msgbox.setText("出现重复")
                msgbox.setDetailedText("原始：\n %s %s\n 新：\n %s %s" %(self.list.item(trow, 0).text(),
                                                                        self.list.item(trow, 1).text(), snum, answers))
                abort_button = msgbox.addButton("放弃", QtGui.QMessageBox.ActionRole)
                replace_button = msgbox.addButton("替换", QtGui.QMessageBox.ActionRole)
                msgbox.exec_()
                if msgbox.clickedButton() == abort_button:
                    return
                elif msgbox.clickedButton() == replace_button:
                    row = trow
                    print(row)
            else:
                self.list.insertRow(row)
            if self.list.columnCount() < 2:
                self.list.insertColumn(0)
                self.list.insertColumn(1)
                self.list.setColumnWidth(0, 100)
                self.list.setColumnWidth(1, 700)
            print(snum, answers)
            self.list.setItem(row, 0, QtGui.QTableWidgetItem(snum))
            self.list.setItem(row, 1, QtGui.QTableWidgetItem(answers))
            self.list.selectRow(row)
            self.image_show.start_capture()

    def save_test(self):
        self.image_show.stop_capture()
        filename = QtGui.QFileDialog.getSaveFileName(filter="*.xlsx")
        if filename:
            writer = xlsxwriter.Workbook(filename)
            sheet = writer.add_worksheet()
            for i in range(self.list.rowCount()):
                for j in range(self.list.columnCount()):
                    sheet.write(i, j, self.list.item(i, j).text())
            writer.close()
            QtGui.QMessageBox.information(self,"PyQt", "完成")
        self.image_show.start_capture()



class CardReader():
    def __init__(self, template_a, template_b, abmark):
        self.template_a = cardscan.read_template(template_a)
        self.template_b = cardscan.read_template(template_b)
        self.abmark = cardscan.read_abmark(abmark)

    def read_card(self, image):
        cardtype=cardscan.abcheck(image, self.abmark)
        print(cardtype)
        if cardtype == "B":
            template_info = self.template_b
        elif cardtype == "A":
            template_info = self.template_a

        snum, answer = cardscan.read_sheet(image, template_info)
        return (snum, cardscan.convert_compact_form(answer))

if __name__ == "__main__":
    template_a = ('cards/eighty-fullA.json')
    template_b = ('cards/eighty-fullB.json')
    abmark = ('cards/abmark.json')

    app = QtGui.QApplication(sys.argv)
    cardreader = CardReader(template_a, template_b, abmark)
    scanner = CardScanner(cardreader=cardreader)
    scanner.show()
    app.exec_()
